#include "common/defines.h"
#include "converseabstractconversation.h"

#include "conversetextmessage.h"
#include "conversephotomessage.h"

#include "appchat/accountmanager.h"
#include "account/accountuser.h"
#include "network/networkmanager.h"
#include "network/networkresponse.h"
#include "conversemanager.h"
#include "conversemessagegroup.h"
#include "conversemessagecontainer.h"

#include "request/gethistoryrequest.h"
#include "response/gethistoryresponse.h"

#include "request/messagerequest.h"
#include "event/messageevent.h"

#include "request/messagegrouprequest.h"

#include "contact/contactmanager.h"
#include "contact/contactabstractcontact.h"

#include "service/actionservice.h"


#include <QDateTime>
#include <QDebug>


namespace Converse {

AbstractConversation::AbstractConversation(qint64 id, Contact::AbstractContact *contact, QObject *parent)
    : QObject(parent)
    , m_id(id)
    , m_ownerContact(contact)

{
    init();
}


AbstractConversation::~AbstractConversation()
{
    delete m_messageContainer;
    m_messageContainer = 0;
}


void AbstractConversation::init()
{
    m_messageContainer = new MessageContainer(this);
    setConnection();
}


void AbstractConversation::setConnection() const
{
    connect(m_messageContainer,
            &MessageContainer::dataAboutToBeInserted,
            this,
            &AbstractConversation::onDataAboutToBeInserted);

    connect(m_messageContainer,
            &MessageContainer::dataInserted,
            this,
            &AbstractConversation::onDataInserted);

    connect(m_messageContainer,
            &MessageContainer::dataAboutToBeUpdated,
            this,
            &AbstractConversation::onDataAboutToBeUpdated);

    connect(m_messageContainer,
            &MessageContainer::dataUpdated,
            this,
            &AbstractConversation::onDataUpdated);

    connect(this,
            &Converse::AbstractConversation::messageReadFromLocal,
            Manager::instance(),
            &Converse::Manager::onMessageReadFromLocal);

    connect(this,
            &Converse::AbstractConversation::messageReceived,
            Manager::instance(),
            &Converse::Manager::onMessageReceived);

    connect(this,
            &Converse::AbstractConversation::messageSent,
            Manager::instance(),
            &Converse::Manager::onMessageSent);

    connect(this,
            &Converse::AbstractConversation::messageSend,
            Manager::instance(),
            &Converse::Manager::onMessageSend);

    connect(this,
            &Converse::AbstractConversation::dataInserted,
            Manager::instance(),
            &Converse::Manager::onDataInserted);

    connect(this,
            &Converse::AbstractConversation::buzz,
            Service::ActionService::instance(),
            &Service::ActionService::onBuzz);
}


bool AbstractConversation::send(const QString &text, qint64 createdTime)
{
    TextMessage *message = new TextMessage(-1,
                                           createdTime,
                                           *this,
                                           createdTime,
                                           createdTime,
                                           AccountManager::instance()->getUserId(),
                                           toId(),
                                           text,
                                           this);

    m_sendingFinder.insert(createdTime, message);

    m_messageContainer->append(message);

    ///
    /// gui signal bao gui message de day luon len form
    ///

    AbstractMessage* aMessageSend = (AbstractMessage*)(message);
    emit messageSend(aMessageSend);

    MessageRequest *request = new MessageRequest(*message, this);
    if (!Network::Manager::instance()->post(request)) {
        delete request;
        request = 0;
        return false;
    }

    m_sendingRequestFinder.insert(createdTime, request);
    return true;
}


bool AbstractConversation::send(const QString &text,
                                qint64 createdTime,
                                const QString imageID,
                                QPixmap pic)
{
    PhotoMessage *message = new PhotoMessage(-1,
                                             createdTime,
                                             *this,
                                             createdTime,
                                             createdTime,
                                             AccountManager::instance()->getUserId(),
                                             toId(),
                                             text,
                                             this);

    message->setPhoto(pic);
    message->setFomat(text);
    message->setPhotoId(imageID);

    m_sendingFinder.insert(createdTime, message);

    MessageRequest *request = new MessageRequest(*message, this);
    if (!Network::Manager::instance()->post(request)) {
        delete request;
        request = 0;
    }
    return true;
}


bool AbstractConversation::sendGroup(const QString &text, qint64 createdTime)
{
    TextMessage *message = new TextMessage(-1,
                                           createdTime,
                                           *this,
                                           createdTime,
                                           createdTime,
                                           AccountManager::instance()->getUserId(),
                                           toId(),
                                           text,
                                           this);

    m_sendingFinder.insert(createdTime, message);

    m_messageContainer->append(message);

    ///
    /// gui signal bao gui message de day luon len form
    ///

    AbstractMessage* aMessageSend = (AbstractMessage*)(message);
    emit messageSend(aMessageSend);

    MessageGroupRequest *request = new MessageGroupRequest(*message, this);
    if (!Network::Manager::instance()->post(request)) {
        delete request;
        request = 0;
        return false;
    }

    m_sendingGroupRequestFinder.insert(createdTime, request);
    return true;
}



void AbstractConversation::receive(const MessageEvent &event)
{
    qDebug() << "void AbstractConversation::receive before";
    // Message existed
    Finder::Iterator iterator = m_messageFinder.find(event.id());
    if (iterator != m_messageFinder.end())
        return;

    qint64 sendingId = event.sendingId();
    qint64 id = event.id();
    iterator = m_sendingFinder.find(sendingId);

    // Sent message
    if (iterator != m_sendingFinder.end()) {
        AbstractMessage *sendingMessage = *iterator;
        Q_ASSERT(sendingMessage);
        int createdTime = sendingMessage->createdTime();

        // Delete message request
        MessageRequestFinder::Iterator requestIterator = m_sendingRequestFinder.find(createdTime);
        if (requestIterator != m_sendingRequestFinder.end()) {
            delete *requestIterator;
            m_sendingRequestFinder.remove(createdTime);
        }

        MessageGroupRequestFinder::Iterator requestGroupIterator = m_sendingGroupRequestFinder.find(createdTime);
        if (requestGroupIterator != m_sendingGroupRequestFinder.end()) {
            delete *requestGroupIterator;
            m_sendingGroupRequestFinder.remove(createdTime);
        }

        sendingMessage->setId(id);
        sendingMessage->setReceivedTime(event.createdTime());


        m_sendingFinder.remove(sendingId);
        m_messageFinder.insert(id, sendingMessage);
        emit messageSent(sendingMessage);
        return;
    }

    // Received Message
    AbstractMessage *receivingMessage = event.createMessage(*this, this);

    if (receivingMessage->id() != id) {
        delete receivingMessage;
        return;
    }

    if (receivingMessage->type() == AbstractMessage::TextType) {
        QString messageContent = receivingMessage->message();
        if (messageContent == "<buzz!!!>") {
            emit buzz();
        }
    }

    m_messageContainer->append(receivingMessage);
    m_messageFinder.insert(id, receivingMessage);

    // Count unread message if ownercontact of this conversation is not active
    if (!Contact::Manager::instance()->isActiveContact(this->contact())) {
        Q_ASSERT(this->contact());
        this->contact()->increaseUnreadMessage(1);
    }




    emit messageReceived(receivingMessage);

    qDebug() << "void AbstractConversation::receive after" << receivingMessage->message();
}


void AbstractConversation::receive(qint64 id,
                                   qint64 sendingId,
                                   qint64 createdTime,
                                   qint64 senderId,
                                   qint64 receiverId,
                                   const QString &text)
{
    qDebug() << "void AbstractConversation::receive before";
    Finder::Iterator iterator = m_messageFinder.find(id);
    if (iterator != m_messageFinder.end())
        return;

    iterator = m_sendingFinder.find(sendingId);
    if (iterator != m_sendingFinder.end()) {
        AbstractMessage *message = *iterator;
        Q_ASSERT(message);
        if (dynamic_cast<TextMessage *>(message) == NULL)
            return;
        message->setReceivedTime(createdTime);
        message->setId(id);
        m_sendingFinder.remove(sendingId);
        m_messageFinder.insert(id, message);
        emit messageSent(message);
        return;
    }


    TextMessage *message = new TextMessage(id,
                                           sendingId,
                                           *this,
                                           createdTime,
                                           createdTime,
                                           senderId,
                                           receiverId,
                                           text,
                                           this);

    m_messageContainer->append(message);
    m_messageFinder.insert(id, message);

    // Count unread message if ownercontact of this conversation is not active
    if (!Contact::Manager::instance()->isActiveContact(this->contact())) {
        Q_ASSERT(this->contact());
        this->contact()->increaseUnreadMessage(1);
    }
}


void AbstractConversation::itemAboutToBeInserted(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index > count()))
        return;

    emit dataAboutToBeInserted(index, messageGroup);
}


void AbstractConversation::itemInserted(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index > count()))
        return;

    emit dataInserted(index, messageGroup);
}


void AbstractConversation::itemAboutToBeUpdated(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index >= count()))
        return;

    emit dataAboutToBeUpdated(index, messageGroup);
}


void AbstractConversation::itemUpdated(int index, MessageGroup *messageGroup)
{
    if (messageGroup == NULL)
        return;

    if ((index < 0) || (index >= count()))
        return;

    emit dataUpdated(index, messageGroup);
}


bool AbstractConversation::hasSending(qint64 id) const
{
    return m_sendingFinder.find(id) != m_sendingFinder.end();
}


void AbstractConversation::postGetHistoryRequest(int messageCount)
{
    qint64 partnerId = toId();
    qint64 lastId = 0;
    GetHistoryRequest *request = new GetHistoryRequest(partnerId,
                                                       messageCount,
                                                       lastId);
    connect(request,
            &GetHistoryRequest::responded,
            this,
            &AbstractConversation::onGetHistoryResponded);

    if (!Network::Manager::instance()->post(request)) {
        delete request;
        request = 0;
    }
}


void AbstractConversation::onGetHistoryResponded(Network::HttpRequest *request)
{
    const Network::HttpRequest &constRequest = *request;
    const Network::Response &response = constRequest.response();

    const GetHistoryResponse &historyResponse = (const GetHistoryResponse &)response;
    const GetHistoryResponse::Items &messageItemList = historyResponse.items();


    int position = messageItemList.count() - 1;
    GetHistoryResponse::Item *messageItem = NULL;
    for (; position >= 0; --position) {
        messageItem = messageItemList.at(position);
        Q_ASSERT(messageItem);
        qint64 id = messageItem->id();
        qint64 sendingId = -1;
        qint64 createdTime = messageItem->created();
        qint64 receiverId = messageItem->receiverId();
        qint64 senderId = messageItem->senderId();
        QString messageContent = messageItem->text();

        if ((senderId <= 0) || (receiverId <= 0) || (id <= 0))
            continue;

        TextMessage *message = new TextMessage(id,
                                               sendingId,
                                               *this,
                                               createdTime,
                                               createdTime,
                                               senderId,
                                               receiverId,
                                               messageContent,
                                               this);

        m_messageContainer->insert(message);
        m_messageFinder.insert(id, message);
    }

    request->deleteLater();
}

void AbstractConversation::onGetHistoryMeessageFromLocal(qint64 id,
                                                         qint64 conversationId,
                                                         qint64 sendingId,
                                                         qint64 createdTime,
                                                         qint64 receivedTime,
                                                         int32_t senderId,
                                                         qint64 receiverId,
                                                         const QString &text,
                                                         int typeMsg,
                                                         int isRead,
                                                         int isSent)
{
    Q_UNUSED(isRead);
    Q_UNUSED(isSent);

    this->setId(conversationId);

    if (typeMsg == 0) {
        TextMessage *message = new TextMessage(id,
                                               sendingId,
                                               *this,
                                               createdTime,
                                               receivedTime,
                                               senderId,
                                               receiverId,
                                               text,
                                               this);

        m_messageContainer->insert(message);
        m_messageFinder.insert(id, message);

        emit messageReadFromLocal(message);

        qDebug() << "*****************AbstractConversation::onGetHistoryMeessageFromLocal";
    }
}

void AbstractConversation::onDataAboutToBeInserted(int index, MessageGroup *messageGroup)
{
    itemAboutToBeInserted(index, messageGroup);
}


void AbstractConversation::onDataInserted(int index, MessageGroup *messageGroup)
{
    itemInserted(index, messageGroup);
}


void AbstractConversation::onDataAboutToBeUpdated(int index, MessageGroup *messageGroup)
{
    itemAboutToBeUpdated(index, messageGroup);
}


void AbstractConversation::onDataUpdated(int index, MessageGroup *messageGroup)
{
    itemUpdated(index, messageGroup);
}


void AbstractConversation::setId(qint64 id)
{
    m_id = id;
}


qint64 AbstractConversation::id() const
{
    return m_id;
}


void AbstractConversation::setContact(Contact::AbstractContact *contact)
{
    m_ownerContact = contact;
}


Contact::AbstractContact *AbstractConversation::contact() const
{
    return m_ownerContact;
}


const AbstractConversation::MessageGroupList &AbstractConversation::dataItemList() const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->dataItemList();
}


MessageContainer *AbstractConversation::messageContainer() const
{
    return m_messageContainer;
}


bool AbstractConversation::validIndex(int index) const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->validIndex(index);
}


bool AbstractConversation::contains(MessageGroup *messageGroup) const
{
    if (messageGroup == NULL)
        return false;

    Q_ASSERT(m_messageContainer);
    return m_messageContainer->contains(messageGroup);
}


int AbstractConversation::indexOf(MessageGroup *messageGroup) const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->indexOf(messageGroup);
}


MessageGroup *AbstractConversation::at(int index) const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->at(index);
}


int AbstractConversation::size() const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->size();
}


int AbstractConversation::count() const
{
    Q_ASSERT(m_messageContainer);
    return m_messageContainer->count();
}

} // namespace Converse
